/* coiffeur.c */
#include <stdio.h>
#include <pthread.h>
#include "semaphore.h"

//#define NbTh 10      //Nombre de processus symbolisant les clients
//#define N  8	//Nombre de chaises dans le salon de coiffure

void departAutomatique();
void departManuel();

typedef enum {noTrain,TGV, GL, M} Type;
typedef struct Train Train;
struct Train{
	pthread_t thread;
	Type type;
	int num;
};

//intialisation 
int nbTrainAttVoieA=0;
int nbTrainVoieC=0;
int nbTrainAig1_OE=0;
int nbTrainAig1_EO=0;
int nbTrainAig2_OE=0;
int nbTrainAig2_EO=0;
int nbTgvVoieGarage_OE=0;
int nbTgvVoieGarage_EO=0;
int nbGlVoieGarage_OE=0;
int nbGlVoieGarage_EO=0;
int nbMVoieGarage_OE=0;
int nbMVoieGarage_EO=0;
int nbTrainTunnel_OE=0;
int nbTrainTunnel_EO=0;
int nbTrainAttTunnel=0;
int nbTgvTotal_OE=0;
int nbTgvTotal_EO=0;



//Les Mutex
pthread_mutex_t mtxTGV,mtxAig1, mtxAig2, mtxTunnel,mtxLigne;
//Semaphore semTGV,semGL;
//Semaphore semLigneTGV,semLigneGL
//Les conditions
pthread_cond_t 	condTrainAttAig2,condTrainAttLigne,condTrainAttVoieA,
		condTgvAttTunnel,condTgvAttAig2,
		condGlAttTunnel,condGlAttAig2,
		condMAttTunnel,condMAttAig1;

/*******************************************************************************************/
/********************************>>  TGV Est ----> Ouest  <<********************************/
/*******************************************************************************************/
void tgvEstOuest(Train *train){
	
	printf("Le TGV %d veut rentrer sur la ligne\n",train->num);
	pthread_mutex_lock(&mtxTGV);
	if(nbTgvTotal_OE>0) pthread_cond_wait(&condTrainAttLigne,&mtxTGV);
	nbTrainAttTunnel--;
	pthread_mutex_lock(&mtxTunnel);
	nbTrainTunnel_EO++;
	printf("Le TGV %d est sur la ligne\n",train->num);
	sleep(2);
	printf("Le TGV %d entre dans le tunnel\n",train->num);
	sleep(2);
	nbTrainTunnel_EO--;
	pthread_mutex_unlock(&mtxTunnel);
		if(nbTgvVoieGarage_OE>0){ pthread_cond_signal(&condTgvAttTunnel);}
	else	if(nbGlVoieGarage_OE>0){ pthread_cond_signal(&condGlAttTunnel);}
	else	if(nbMVoieGarage_OE>0){	pthread_cond_signal(&condMAttTunnel);}
	else 	pthread_cond_signal(&condTrainAttLigne);
	printf("Le TGV %d sort du tunnel \n",train->num);
	nbTgvVoieGarage_EO++;
	printf("Le TGV %d est sur les voies de garages du TGV\n",train->num);
	sleep(2);
	if(nbTrainAig2_EO>0 || nbTrainAig2_OE>0){
		printf("Le TGV %d attend l'aiguillage 2\n",train->num); 
		pthread_cond_wait(&condTgvAttAig2,&mtxAig2);
	}
	pthread_mutex_lock(&mtxAig2);
	nbTgvVoieGarage_EO--;
	printf("Le TGV %d est sur aig2\n",train->num);
	sleep(2);
	pthread_mutex_unlock(&mtxAig2);
	if (nbTgvVoieGarage_EO>0) pthread_cond_signal(&condTgvAttAig2);
	else if (nbGlVoieGarage_EO>0) pthread_cond_signal(&condGlAttAig2);
	else if (nbTrainVoieC>0) pthread_cond_signal(&condTrainAttAig2);
	printf("Le TGV %d rentre à la gare\n",train->num);
	nbTgvTotal_EO--;
	if (nbTgvTotal_OE>0) pthread_cond_signal(&condTrainAttLigne);
	pthread_mutex_unlock(&mtxTGV);
	sleep(2);
	printf("Le TGV %d quitte la gare\n",train->num);
}

/*******************************************************************************************/
/********************************>>  TGV Ouest ----> Est  <<********************************/
/*******************************************************************************************/
void tgvOuestEst(Train *train){
	printf("\t\tLe TGV %d est a la gare\n",train->num);
	pthread_mutex_lock(&mtxTGV);
	if(nbTgvTotal_EO>0) pthread_cond_wait(&condTrainAttLigne,&mtxTGV);
	//if (nbTgvCircuit_EO>0) pthread__cond_wait(&condTrainAttLigne,&
	pthread_mutex_lock(&mtxAig2);
	nbTrainVoieC--;
	if(nbTrainAig2_OE>0 || nbTrainAig2_EO>0) pthread_cond_wait(&condTrainAttAig2,&mtxAig2);
	nbTrainAig2_OE++;
	printf("\t\tLe TGV %d est sur aig2\n",train->num);
	sleep(2);
	nbTrainAig2_OE--;
	pthread_mutex_unlock(&mtxAig2);
	nbTgvVoieGarage_OE++;
	printf("\t\tLe TGV %d est sur les voies de garages\n",train->num);
	sleep(2);
	//if(nbTrainTunnel_OE>0||nbTrainTunnel_EO>0) pthread_cond_wait(&condTgvAttTunnel,&mtxTunnel);
	pthread_mutex_lock(&mtxTunnel);
	if(nbTrainTunnel_OE>0||nbTrainTunnel_EO>0) pthread_cond_wait(&condTgvAttTunnel,&mtxTunnel);
	printf("\t\tLe TGV %d entre dans le tunnel \n",train->num);
	nbTgvVoieGarage_OE--;
	nbTrainTunnel_OE++;
	sleep(2);
	printf("\t\tLe TGV %d est sur la ligne \n",train->num);
	sleep(2);
	printf("\t\tLe TGV %d quitte la ligne \n",train->num);
	pthread_mutex_unlock(&mtxTunnel);
	pthread_mutex_unlock(&mtxTGV);
	nbTgvTotal_OE--;
	if (nbTgvTotal_EO>0) pthread_cond_signal(&condTrainAttAig2);
	nbTrainTunnel_OE--;
	if(nbTgvVoieGarage_OE>0) pthread_cond_signal(&condTgvAttTunnel);
	else if (nbGlVoieGarage_OE>0) pthread_cond_signal(&condGlAttTunnel);
	else if (nbMVoieGarage_OE>0) pthread_cond_signal(&condMAttTunnel);
	else if (nbTrainAttTunnel>0) pthread_cond_signal(&condTrainAttLigne);
}

/**************************************************************************************************/
/********************************>>  Grand Line Est ----> Ouest  <<********************************/
/**************************************************************************************************/
void glEstOuest(Train *train){
	printf("Le GL %d veut rentrer sur la ligne\n",train->num);
	nbTrainAttTunnel--;
	pthread_mutex_lock(&mtxTunnel);
	nbTrainTunnel_EO++;
	printf("Le GL %d est sur la ligne\n",train->num);
	sleep(2);
	printf("Le GL %d entre dans le tunnel\n",train->num);
	sleep(2);
	nbTrainTunnel_EO--;
	pthread_mutex_unlock(&mtxTunnel);
		if(nbTgvVoieGarage_OE>0){ pthread_cond_signal(&condTgvAttTunnel);}
	else	if(nbGlVoieGarage_OE>0){ pthread_cond_signal(&condGlAttTunnel);}
	else	if(nbMVoieGarage_OE>0){	pthread_cond_signal(&condMAttTunnel);}
	else 	pthread_cond_signal(&condTrainAttLigne);
	printf("Le GL %d sort du tunnel \n",train->num);
	nbGlVoieGarage_EO++;
	printf("Le GL %d est sur les voies de garages du GL\n",train->num);
	sleep(2);
	if(nbTrainAig2_EO>0 || nbTrainAig2_OE>0){
		printf("Le GL %d attend l'aiguillage 2\n",train->num); 
		pthread_cond_wait(&condGlAttAig2,&mtxAig2);
	}
	pthread_mutex_lock(&mtxAig2);
	nbGlVoieGarage_EO--;
	printf("Le GL %d est sur aig2\n",train->num);
	sleep(2);
	pthread_mutex_unlock(&mtxAig2);
	if (nbTgvVoieGarage_EO>0) pthread_cond_signal(&condTgvAttAig2);
	else if (nbGlVoieGarage_EO>0) pthread_cond_signal(&condGlAttAig2);
	else if (nbTrainVoieC>0) pthread_cond_signal(&condTrainAttAig2);
	printf("Le GL %d rentre à la gare\n",train->num);
	sleep(2);
	printf("Le GL %d quitte la gare\n",train->num);
}

/*********************************************************************************************/
/*****************************>>  Marchandise Est ----> Ouest  <<*****************************/
/*********************************************************************************************/
void mEstOuest(Train *train){
	printf("Le M %d veut rentrer sur la ligne\n",train->num);
	nbTrainAttTunnel--;
	pthread_mutex_lock(&mtxTunnel);
	nbTrainTunnel_EO++;
	printf("Le M %d est sur la ligne\n",train->num);
	sleep(2);
	printf("Le M %d entre dans le tunnel\n",train->num);
	sleep(2);
	nbTrainTunnel_EO--;
	pthread_mutex_unlock(&mtxTunnel);
		if(nbTgvVoieGarage_OE>0){ pthread_cond_signal(&condTgvAttTunnel);}
	else	if(nbGlVoieGarage_OE>0){ pthread_cond_signal(&condGlAttTunnel);}
	else	if(nbMVoieGarage_OE>0){	pthread_cond_signal(&condMAttTunnel);}
	else 	pthread_cond_signal(&condTrainAttLigne);
	printf("Le M %d sort du tunnel \n",train->num);
	nbMVoieGarage_EO++;
	printf("Le M %d est sur les voies de garages M\n",train->num);
	sleep(2);
	if(nbTrainAig1_EO>0 || nbTrainAig1_OE>0){
		printf("Le M %d attend l'aiguillage 1\n",train->num);
		pthread_cond_wait(&condMAttAig1,&mtxAig1);
	}
	nbMVoieGarage_EO--;
	pthread_mutex_lock(&mtxAig1);
	nbTrainAig1_EO++;
	printf("Le M %d est sur aig1\n",train->num);
	sleep(2);
	pthread_mutex_unlock(&mtxAig1);
	nbTrainAig1_EO--;
	if(nbTrainAttVoieA>0) pthread_cond_signal(&condTrainAttVoieA);
	else if(nbMVoieGarage_EO>0) pthread_cond_signal(&condMAttAig1);
	printf("Le M %d rentre à la voie B\n",train->num);
	sleep(2);
	printf("Le M %d quitte la voie B\n",train->num);
}

/************************************************************************************************/
/*******************************>>  Grand Line Ouest ----> Est  <<*******************************/
/************************************************************************************************/
void glOuestEst(Train *train){
	printf("\t\tLe GL %d est a la gare\n",train->num);
	pthread_mutex_lock(&mtxAig2);
	if(nbTrainAig2_OE>0 || nbTrainAig2_EO>0) pthread_cond_wait(&condTrainAttAig2,&mtxAig2);
	nbTrainAig2_OE++;
	printf("\t\tLe GL %d est sur aig2\n",train->num);
	sleep(2);
	nbTrainAig2_OE--;
	pthread_mutex_unlock(&mtxAig2);
	nbGlVoieGarage_OE++;
	printf("\t\tLe GL %d est sur les voies de garages GL\n",train->num);
	sleep(2);
	if(nbTrainTunnel_OE>0||nbTrainTunnel_EO>0) pthread_cond_wait(&condGlAttTunnel,&mtxTunnel);
	pthread_mutex_lock(&mtxTunnel);
	printf("\t\tLe GL %d entre dans le tunnel \n",train->num);
	nbGlVoieGarage_OE--;
	nbTrainTunnel_OE++;
	sleep(2);
	printf("\t\tLe GL %d est sur la ligne \n",train->num);
	sleep(2);
	printf("\t\tLe GL %d quitte la ligne \n",train->num);
	pthread_mutex_unlock(&mtxTunnel);
	nbTrainTunnel_OE--;
	if(nbTgvVoieGarage_OE>0) pthread_cond_signal(&condTgvAttTunnel);
	else if (nbGlVoieGarage_OE>0) pthread_cond_signal(&condGlAttTunnel);
	else if (nbMVoieGarage_OE>0) pthread_cond_signal(&condMAttTunnel);
	else if (nbTrainAttTunnel>0) pthread_cond_signal(&condTrainAttLigne);
}

/***********************************************************************************************/
/******************************>>  Marchandise Ouest ----> Est  <<******************************/
/***********************************************************************************************/
void mOuestEst(Train *train){
	//Le train M n'est pas censé aller à la gare
	printf("\t\tLe M %d attend pour entre est a la gare\n",train->num);
	pthread_mutex_lock(&mtxAig1);
	if (nbTrainAig1_EO>0 || nbTrainAig1_OE>0) pthread_cond_wait(&condTrainAttVoieA,&mtxAig1);
	nbTrainAttVoieA--;
	printf("\t\tLe M %d est sur le voie A\n",train->num);
	sleep(2);
	nbTrainAig1_OE++;
	printf("\t\tLe M %d est sur aig1\n",train->num);
	sleep(2);
	nbTrainAig1_OE--;
	pthread_mutex_unlock(&mtxAig2);
	nbMVoieGarage_OE++;
	printf("\t\tLe M %d est sur les voies de garages\n",train->num);
	sleep(2);
	if(nbTrainTunnel_OE>0||nbTrainTunnel_EO>0) pthread_cond_wait(&condMAttTunnel,&mtxTunnel);
	pthread_mutex_lock(&mtxTunnel);
	nbMVoieGarage_OE--;	
	printf("\t\tLe M %d entre dans le tunnel \n",train->num);
	nbTrainTunnel_OE++;
	sleep(2);
	printf("\t\tLe M %d est sur la ligne \n",train->num);
	sleep(2);
	printf("\t\tLe M %d quitte la ligne \n",train->num);
	pthread_mutex_unlock(&mtxTunnel);
	nbTrainTunnel_OE--;
	if(nbTgvVoieGarage_OE>0) pthread_cond_signal(&condTgvAttTunnel);
	else if (nbGlVoieGarage_OE>0) pthread_cond_signal(&condGlAttTunnel);
	else if (nbMVoieGarage_OE>0) pthread_cond_signal(&condMAttTunnel);
	else if (nbTrainAttTunnel>0) pthread_cond_signal(&condTrainAttLigne);
}

/************************************************************************************************/
/*****************************>>  Fonction Train Est ----> Ouest  <<*****************************/
/************************************************************************************************/
void * FoncEstOuest(void *data){
	Train *train = (struct Train*) data;
	int priorite = (int) train->type;
	nbTrainAttTunnel++;
	//si priorite=1 cad TGV
	if(priorite==1){
		nbTgvTotal_EO++;
		tgvEstOuest(train);
	}
	//si priorite=2 cad GL
	if(priorite==2){
		glEstOuest(train);
	}
	//si priorite=3 cad M
	if(priorite==3){
		mEstOuest(train);
	}
}

/************************************************************************************************/
/*****************************>>  Fonction Train Ouest ----> Est  <<*****************************/
/************************************************************************************************/
void * FoncOuestEst(void *data)
{  
	Train *train = (struct Train*) data;
	int priorite = (int) train->type;                        
	//si priorite=1 cad TGV
	if(priorite==1){
		nbTgvTotal_OE++;
		nbTrainVoieC++;
		tgvOuestEst(train);
	}
	//si priorite=2 cad GL
	if(priorite==2){
		nbTrainVoieC++;
		glOuestEst(train);
	}
	//si priorite=3 cad M
	if(priorite==3){
		nbTrainAttVoieA++;
		mOuestEst(train);
	}
}

int main(){
	int i=0;

	//Initialisation des Mutex
	pthread_mutex_init(&mtxAig1,0);
	pthread_mutex_init(&mtxAig2,0);
	pthread_mutex_init(&mtxTunnel,0);
	pthread_mutex_init(&mtxLigne,0);
	pthread_mutex_init(&mtxTGV,0);

	//Initialisation des conditions
	
	pthread_cond_init(&condTrainAttAig2,0);
	pthread_cond_init(&condTrainAttLigne,0);
	pthread_cond_init(&condTrainAttVoieA,0);
	pthread_cond_init(&condTgvAttTunnel,0);
	pthread_cond_init(&condTgvAttAig2,0);
	pthread_cond_init(&condGlAttTunnel,0);
	pthread_cond_init(&condGlAttAig2,0);
	pthread_cond_init(&condMAttTunnel,0);
	pthread_cond_init(&condMAttAig1,0);

	/*Creation d'un train de type Train
	Train *tabTrain[10];
	for(i=0;i<10;i++){
		printf("donner le priorite du Train %d\n",i);
		scanf("%d",&tabTrain[i].type);
	}*/

	//departAutomatique();
	departManuel();
	
	/* liberation des ressources");*/
	pthread_mutex_destroy(&mtxAig1);
	pthread_mutex_destroy(&mtxAig2);
	pthread_mutex_destroy(&mtxTunnel);
	pthread_mutex_destroy(&mtxLigne);
	pthread_mutex_destroy(&mtxTGV);
	
	pthread_cond_destroy(&condTrainAttAig2);
	pthread_cond_destroy(&condTrainAttLigne);
	pthread_cond_destroy(&condTrainAttVoieA);
	pthread_cond_destroy(&condTgvAttTunnel);
	pthread_cond_destroy(&condTgvAttAig2);
	pthread_cond_destroy(&condGlAttTunnel);
	pthread_cond_destroy(&condGlAttAig2);
	pthread_cond_destroy(&condMAttTunnel);
	pthread_cond_destroy(&condMAttAig1);

	printf("nbTrainAttVoieA= %d\nnbTrainVoieC= %d\nnbTrainAig1_OE= %d\n",nbTrainAttVoieA,nbTrainVoieC,nbTrainAig1_OE);
printf("nbTrainAig1_EO= %d\nnbTrainAig2_OE= %d\nnbTrainAig2_EO= %d\n",nbTrainAig1_EO,nbTrainAig2_OE,nbTrainAig2_EO);
printf("nbTgvVoieGarage_OE= %d\nnbTgvVoieGarage_EO= %d\nnbGlVoieGarage_OE= %d\n",nbTgvVoieGarage_OE,nbTgvVoieGarage_EO,nbGlVoieGarage_OE);
printf("nbGlVoieGarage_EO= %d\nnbMVoieGarage_OE= %d\nnbMVoieGarage_EO= %d\n",nbGlVoieGarage_EO,nbMVoieGarage_OE,nbMVoieGarage_EO);
printf("nbTrainTunnel_OE= %d\nnbTrainTunnel_EO= %d\nnbTrainAttTunnel= %d\n",nbTrainTunnel_OE,nbTrainTunnel_EO,nbTrainAttTunnel);
	
	//libereSem()
	exit(0);
}

void departManuel(){
	
	Train monTrain;
	monTrain.type=TGV;
	monTrain.num=1;

	Train monTrain2;
	monTrain2.type=TGV;
	monTrain2.num=2;
	
	Train monTrain3;
	monTrain3.type=GL;
	monTrain3.num=3;

	Train monTrain4;
	monTrain4.type=GL;
	monTrain4.num=4;
	
	Train monTrain5;
	monTrain5.type=M;
	monTrain5.num=5;

	Train monTrain6;
	monTrain6.type=M;
	monTrain6.num=6;



	//creation du thread
	pthread_create(&(monTrain.thread), NULL,(void *(*)())FoncEstOuest, (void*) &monTrain);
	pthread_create(&(monTrain2.thread), NULL,(void *(*)())FoncOuestEst, (void*) &monTrain2);
	//pthread_create(&(monTrain3.thread), NULL,(void *(*)())FoncEstOuest, (void*) &monTrain3);
//	pthread_create(&(monTrain4.thread), NULL,(void *(*)())FoncOuestEst, (void*) &monTrain4);
	//pthread_create(&(monTrain5.thread), NULL,(void *(*)())FoncEstOuest, (void*) &monTrain5);
	//pthread_create(&(monTrain6.thread), NULL,(void *(*)())FoncOuestEst, (void*) &monTrain6);
	//attend la fin du thread monTrain
	pthread_join(monTrain.thread,NULL);
	pthread_join(monTrain2.thread,NULL);
	pthread_join(monTrain3.thread,NULL);
	pthread_join(monTrain4.thread,NULL);
	pthread_join(monTrain5.thread,NULL);
	pthread_join(monTrain6.thread,NULL);

	//Il manque des pthread_join

}

void departAutomatique(){
	
	//Début de test pour la création automatique de plusieurs trains
	int nbTrain = 9;
	int i = 0;
	Train tabTrain[nbTrain];
	for(i=0;i<nbTrain;i+=3){
		tabTrain[i].type=TGV;
		tabTrain[i].num=i;
		tabTrain[i+1].type=M;
		tabTrain[i+1].num=i;
		tabTrain[i+2].type=GL;
		tabTrain[i+2].num=i;
	}

	int estOuest = 0;
	for(i=0;i<nbTrain;i++){
		if(estOuest==0){
			estOuest = 1;
			pthread_create(&(tabTrain[i].thread), NULL,(void *(*)())FoncOuestEst, (void*) &tabTrain[i]);
		}else{
			estOuest = 0;
			pthread_create(&(tabTrain[i].thread), NULL,(void *(*)())FoncEstOuest, (void*) &tabTrain[i]);
		}
	}
	for(i=0;i<nbTrain;i ++)
        	pthread_join(tabTrain[i].thread,NULL);
}
